﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SQLiteCoreDB;
using SQLiteCoreDB.Models;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO.Abstractions;
using WebFile.Filter;
using WebFile.Models;

namespace WebFile.Controllers
{
    /// <summary>
    /// 文件上传处理
    /// </summary>
    /// 添加时间：2023-8-31 18:00:46

    public class FileController : Controller
    {
        private const int MAX_WAIT_TIME_SECONDS = 60; // 合并文件等待时间（秒）
        // 视图
        public ActionResult Preview()
        {
            return View();
        }


        // 视图，上传页面
        public ActionResult Upload(string dir)
        {

            ViewBag.dir = dir;
            return View();
        }

        //创建文件夹
        //添加时间：2023-9-13 13:52:06，

        public ActionResult MkdirFolder(string folder, string dir)
        {
            if (string.IsNullOrWhiteSpace(folder))
            {
                return Json(new Result("文件夹名称不可为空"));
            }
            bool right = folder.Contains("\\")
                                || folder.Contains("/")
                                || folder.Contains(":")
                                || folder.Contains("*")
                                || folder.Contains("?")
                                || folder.Contains("\"")
                                || folder.Contains("<")
                                || folder.Contains(">")
                                || folder.Contains("|");
            if (right)
            {
                return Json(new Result("文件夹名称，（英文符号）不允许包含\\ / : * ? “ < > |"));
            }
            try
            {
                folder = folder.Trim();
                if (!string.IsNullOrWhiteSpace(dir))
                {
                    dir = dir.Trim();
                }
                dir ??= "";
                string dirNew = Path.Combine(KeyValueConfig.FileSaveDir, dir, folder);
                if (Directory.Exists(dirNew))
                {
                    return Json(new Result("已存在，不用创建", 1, 1));
                }
                else
                {
                    Directory.CreateDirectory(dirNew);
                    return Json(new Result("创建成功", 1, 2));
                }
            }
            catch (Exception ex)
            {
                var ex1 = ex.InnerException ?? ex;
                return Json(new Result(ex1.Message));
            }
        }

        /// <summary>
        /// 并发合并检查
        /// </summary>
        static ConcurrentDictionary<string, string> checkMergeFile = new ConcurrentDictionary<string, string>();



        ////await Task.Yied
        //文件上传，处理
        [HttpPost]

        public async Task<IActionResult> DoUpload()
        {
            // 从Request中获取参数
            try
            {
                var data = Request.Form.Files["data"];
                string name = Request.Form["name"];
                int total = Convert.ToInt32(Request.Form["total"]);
                int index = Convert.ToInt32(Request.Form["index"]);

                // 获取当前文件夹
                string dirCurrent = Uri.UnescapeDataString(Request.Headers["Referer"].ToString()).Split('=').Last();

                await Task.Yield();

                // 保存一个分片到磁盘上
                string dirRoot = KeyValueConfig.FileSaveDir;
                string dir = Path.Combine(dirRoot, dirCurrent);
                string filePath = Path.Combine(dir, $"{name}_{index}");

                // 确保目录存在
                Directory.CreateDirectory(dir);

                await using (var stream = new FileStream(filePath, FileMode.Create))
                {
                    await data.CopyToAsync(stream);
                }

                // 合并文件
                if (total == index)
                {
                    return await MergeFilesAsync(name, dir, total);
                }

                // 返回单个片段上传成功的结果
                return Json(new { Code = 0, Msg = "上传成功" });
            }
            catch (Exception ex)
            {
                return Json(new { Code = 1, Msg = ex.ToString() });
            }
        }

        private async Task<IActionResult> MergeFilesAsync(string name, string dir, int total)
        {
            return await Task.Run(async () =>
            {
                try
                {
                    // 判断是否已经有此文件在合并了，处理并发合并情况
                    lock (checkMergeFile)
                    {
                        if (checkMergeFile.Values.Contains(name))
                        {
                            LogHelpter.AddLog("阻止了一次并发合并文件", null, "Merge_conflict");
                            return Json(new { Code = 1, Msg = "阻止了一次并发合并文件" });
                        }

                        checkMergeFile.TryAdd(Guid.NewGuid().ToString("N"), name);
                    }

                    // 等待文件数对齐总分片
                    WaitForAllParts(name, dir, total);

                    var stopwatch = System.Diagnostics.Stopwatch.StartNew();

                    string finalFilePath = Path.Combine(dir, name);

                    using (var fs = new FileStream(finalFilePath, FileMode.Create))
                    {
                        for (int i = 1; i <= total; ++i)
                        {
                            string partFilePath = Path.Combine(dir, $"{name}_{i}");
                            var bytes = System.IO.File.ReadAllBytes(partFilePath);
                            fs.Write(bytes, 0, bytes.Length);
                            System.IO.File.Delete(partFilePath);
                        }
                    }

                    var user = new FileUser
                    {
                        UserName = LoginMode.username,
                        FileName = name,
                        FileNamePath = finalFilePath
                    };


                    var old = await SL.db.FileUser.FirstOrDefaultAsync(a => a.UserName == LoginMode.username && a.FileName == name);
                    if (old == null)
                    {
                        SL.db.FileUser.Add(user);
                        SL.db.SaveChanges();

                    }

                    LogHelpter.AddLog($"{name} 合并耗时（毫秒）{stopwatch.ElapsedMilliseconds}");

                    return Json(new { Code = 0, Msg = "上传成功", url = GetRelativePath(finalFilePath) });
                }
                catch (Exception ex)
                {
                    return Json(new { Code = 1, Msg = ex.ToString() });
                }
                finally
                {
                    string outValue;
                    checkMergeFile.TryRemove(checkMergeFile.FirstOrDefault(kvp => kvp.Value == name).Key, out outValue);
                }
            });
        }

        private void WaitForAllParts(string name, string dir, int total)
        {
            var stopwatch = Stopwatch.StartNew();
            while (true)
            {
                var files = Directory.GetFiles(dir, $"{name}_*");
                LogHelpter.AddLog($"while----{name}，total={total}，接收文件数={files.Length}");
                if (files.Length == total) break;
                if (stopwatch.Elapsed.TotalSeconds >= MAX_WAIT_TIME_SECONDS)
                {
                    foreach (var item in files)
                    {
                        System.IO.File.Delete(item);
                        LogHelpter.AddLog($"foreach----{item}，total={files.Length}，删除文件。");
                    }
                    LogHelpter.AddLog($"while----{name}，total={total}，等待超时={stopwatch.Elapsed.TotalSeconds}");
                    throw new TimeoutException($"Timed out waiting for all parts of file {name} to be uploaded.");
                }
                Thread.Sleep(1500);

            }

        }

        private string GetRelativePath(string filePath)
        {
            return "/" + filePath.Replace(AppDomain.CurrentDomain.BaseDirectory, "").Replace(@"\", "/").Replace("_" + Path.GetFileName(filePath).Split('_').Last(), "");
        }




    }
}